<template>
  <div class="model-evaluation-container">
    <div class="header">
      <div class="title-description">
        <h2>模型性能评估</h2>
        <p class="description">通过选择模型类型、目标以及测试天数，对单模型性能进行评估。</p>
      </div>
      <div class="control-panel">
        <el-select v-model="modelCategory" placeholder="选择模型类型" @change="handleModelCategoryChange">
          <el-option label="集成学习" value="ensemble" />
          <el-option label="LSTM" value="lstm" />
        </el-select>

        <el-select v-model="selectedTarget" placeholder="选择目标">
          <el-option v-for="target in targetOptions" :key="target.value" :label="target.label" :value="target.value" />
        </el-select>

        <el-input-number v-model="testDays" :min="1" :max="1095" :step="1" :precision="0" placeholder="测试天数"
          v-if="modelCategory === 'ensemble'">
          <template #append>
            <el-tooltip content="请输入1到1095之间的整数" placement="top">
              <el-icon>
                <QuestionFilled />
              </el-icon>
            </el-tooltip>
          </template>
        </el-input-number>

        <el-button type="primary" :loading="isLoading" @click="fetchEvaluation" :disabled="!canEvaluate">
          <template #icon>
            <el-icon>
              <Refresh />
            </el-icon>
          </template>
          执行评估
          <el-tooltip v-if="!canEvaluate" content="请先选择模型类型和必要参数" placement="top">
            <el-icon>
              <WarningFilled />
            </el-icon>
          </el-tooltip>
        </el-button>
        <div class="action-buttons">
          <el-button type="success" @click="exportToWord" :loading="isExporting">
            <template #icon>
              <el-icon>
                <Document />
              </el-icon>
            </template>
            导出Word报告
          </el-button>
        </div>
      </div>
    </div>

    <el-alert v-if="error" :title="error" type="error" show-icon @close="error = ''" class="error-message" />

    <div v-if="evaluationResult" class="result-container">
      <el-card class="metrics-card">
        <template #header>
          <div class="card-header">
            <h3>评估指标</h3>
            <div class="header-tags">
              <el-tag :type="getModelTagType()">
                {{ getModelDisplayName() }}
              </el-tag>
              <el-tooltip :content="getModelDescription()" placement="top">
                <el-icon class="help-icon">
                  <QuestionFilled />
                </el-icon>
              </el-tooltip>
            </div>
          </div>
        </template>

        <div class="metrics-grid">
          <div class="metric-item">
            <div class="metric-label-container">
              <span class="metric-label">MAE{{ modelCategory === 'lstm' ? ' (°C)' : '' }}</span>
              <el-tooltip :content="modelCategory === 'lstm' ?
                '平均绝对误差（Mean Absolute Error），表示预测温度与实际温度的平均绝对偏差' :
                '平均绝对误差（Mean Absolute Error），衡量预测值与实际值之间的平均绝对偏差'" placement="top">
                <el-icon class="help-icon">
                  <QuestionFilled />
                </el-icon>
              </el-tooltip>
            </div>
            <span class="metric-value">
              {{ (evaluationResult.metrics.MAE || 0).toFixed(4) }}
              <el-tooltip content="值越小表示模型预测越准确" placement="top">
                <el-icon class="value-hint">
                  <InfoFilled />
                </el-icon>
              </el-tooltip>
            </span>
          </div>

          <div class="metric-item">
            <div class="metric-label-container">
              <span class="metric-label">R²</span>
              <el-tooltip content="决定系数，表示模型解释目标变量变异的比例" placement="top">
                <el-icon class="help-icon">
                  <QuestionFilled />
                </el-icon>
              </el-tooltip>
            </div>
            <span class="metric-value" :class="{ 'negative': getR2Value() < 0 }">
              {{ getR2Value().toFixed(4) }}
              <el-tooltip :content="getR2Value() < 0 ?
                '负值表示模型表现比简单均值预测还差' :
                '值越接近1表示模型解释力越强'" placement="top">
                <el-icon class="value-hint">
                  <InfoFilled />
                </el-icon>
              </el-tooltip>
            </span>
          </div>

          <div class="metric-item">
            <div class="metric-label-container">
              <span class="metric-label">RMSE{{ modelCategory === 'lstm' ? ' (°C)' : '' }}</span>
              <el-tooltip :content="modelCategory === 'lstm' ?
                '均方根误差（Root Mean Square Error），对温度预测中的较大误差更敏感' :
                '均方根误差（Root Mean Square Error），对较大误差更敏感'" placement="top">
                <el-icon class="help-icon">
                  <QuestionFilled />
                </el-icon>
              </el-tooltip>
            </div>
            <span class="metric-value">
              {{ (evaluationResult.metrics.RMSE || 0).toFixed(4) }}
              <el-tooltip content="值越小表示模型预测越准确" placement="top">
                <el-icon class="value-hint">
                  <InfoFilled />
                </el-icon>
              </el-tooltip>
            </span>
          </div>

          <div class="metric-item">
            <span class="metric-label">测试天数</span>
            <span class="metric-value" v-if="modelCategory === 'ensemble'">
              {{ evaluationResult.metrics.test_days || '-' }}
            </span>
          </div>

          <div class="metric-item">
            <span class="metric-label">模型类型</span>
            <span class="metric-value">
              {{ modelCategory === 'lstm' ? 'LSTM' : '集成学习' }}
              <el-tooltip content="长短期记忆网络，一种递归神经网络" placement="top">
                <el-icon class="value-hint">
                  <InfoFilled />
                </el-icon>
              </el-tooltip>
            </span>
          </div>

          <div class="metric-item">
            <span class="metric-label">预测目标</span>
            <span class="metric-value">
              {{targetOptions.find(t => t.value === selectedTarget)?.label}}
            </span>
          </div>
        </div>
      </el-card>

      <el-card class="plot-card">
        <template #header>
          <div class="card-header">
            <h3>评估图表</h3>
            <el-tooltip :content="modelCategory === 'lstm' ?
              '蓝色曲线为实际温度值，红色曲线为预测温度值' :
              '蓝色曲线为实际值，红色曲线为预测值，绿色点为最大误差位置'" placement="top">
              <el-icon class="help-icon">
                <QuestionFilled />
              </el-icon>
            </el-tooltip>
          </div>
        </template>
        <div class="plot-container">
          <img :src="getFullPlotUrl(evaluationResult.plot_url)" alt="评估图表" class="evaluation-plot"
            @load="handleImageLoad" @error="handleImageError" />
          <el-skeleton v-if="!imageLoaded" :rows="6" animated />
          <div v-if="imageLoaded" class="plot-footer">
            <el-tag size="small" type="info">点击图片可查看大图</el-tag>
          </div>
        </div>
      </el-card>
    </div>

    <div v-else class="empty-state">
      <el-empty description="暂无评估数据">
        <template #image>
          <el-icon :size="100">
            <DataAnalysis />
          </el-icon>
        </template>
        <p>请选择参数后点击"执行评估"按钮</p>
      </el-empty>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import {
  Refresh,
  QuestionFilled,
  WarningFilled,
  InfoFilled,
  DataAnalysis,
  Document
} from '@element-plus/icons-vue';
import { evaluateModel, ModelType, TargetType, EvaluationConfig, ModelMetrics } from '@/api/modelEvaluationService';
import { saveAs } from 'file-saver';
import {
  Document as DocxDocument,
  Packer,
  Paragraph,
  TextRun,
  HeadingLevel,
  Table,
  TableRow,
  TableCell,
  ImageRun,
  AlignmentType
} from 'docx';
import { ElMessage } from 'element-plus';
import { toPng } from 'html-to-image';

// 状态管理
const modelCategory = ref<'ensemble' | 'lstm'>('ensemble');
const selectedTarget = ref<TargetType>(TargetType.GUILIN_MAX);
const testDays = ref<number>(365);
const isLoading = ref(false);
const isExporting = ref(false);
const error = ref('');
const evaluationResult = ref<{
  metrics: ModelMetrics;
  model_type: ModelType;
  plot_url: string;
  status: 'success' | 'error';
  message?: string;
} | null>(null);
const imageLoaded = ref(false);

// 模型类型计算属性
const modelType = computed(() => modelCategory.value === 'lstm' ? ModelType.LSTM : ModelType.ENSEMBLE);

// 目标选项（添加模型类型限制）
const targetOptions = [
  {
    label: '桂林逐日最大温度',
    value: TargetType.GUILIN_MAX,
    modelTypes: [ModelType.ENSEMBLE]
  },
  {
    label: '桂林逐日最低温度',
    value: TargetType.GUILIN_MIN,
    modelTypes: [ModelType.ENSEMBLE]
  },
  {
    label: '桂林逐日平均温度',
    value: TargetType.GUILIN_TEMP,
    modelTypes: [ModelType.LSTM]
  }
];

// 计算属性
const canEvaluate = computed(() => {
  return modelCategory.value && selectedTarget.value;
});

// 方法
const getFullPlotUrl = (relativeUrl: string) => {
  if (!relativeUrl) return '';

  // 统一处理Windows和Unix路径
  const normalizedUrl = relativeUrl.replace(/\\/g, '/');

  return normalizedUrl.startsWith('/')
    ? `http://127.0.0.1:5000${normalizedUrl}`
    : `http://127.0.0.1:5000/${normalizedUrl}`;
};

const handleModelCategoryChange = () => {
  // 重置状态
  evaluationResult.value = null;
  error.value = '';

  // 设置默认目标（基于模型类型）
  const allowedTargets = targetOptions.filter(t => t.modelTypes.includes(modelType.value));
  selectedTarget.value = allowedTargets[0]?.value || TargetType.GUILIN_MAX;
};

const fetchEvaluation = async () => {
  if (!canEvaluate.value) return;

  isLoading.value = true;
  error.value = '';
  evaluationResult.value = null;
  imageLoaded.value = false;

  try {
    // 构建评估配置
    const config: EvaluationConfig = {
      modelType: modelType.value,
      target: selectedTarget.value,
      testDays: modelType.value === ModelType.ENSEMBLE ? testDays.value : undefined
    };

    // 调用统一评估接口
    const result = await evaluateModel(config);

    if (result.status === 'error') {
      throw new Error(result.message || '评估失败');
    }

    // 统一处理响应数据
    evaluationResult.value = {
      metrics: result.metrics,
      model_type: result.model_type,
      plot_url: result.plot_url,
      status: result.status,
      message: result.message
    };
  } catch (err: any) {
    console.error('评估失败:', err);
    error.value = err.response?.data?.message || err.message || '请求评估接口失败';
  } finally {
    isLoading.value = false;
  }
};

const handleImageLoad = () => {
  imageLoaded.value = true;
};

const handleImageError = (e: Event) => {
  console.error('图片加载失败:', (e.target as HTMLImageElement).src);
  imageLoaded.value = false;
};

const getModelTagType = () => {
  return modelCategory.value === 'lstm' ? 'warning' : 'info';
};

const getModelDisplayName = () => {
  return modelCategory.value === 'lstm' ? 'LSTM' : '集成学习';
};

const getModelDescription = () => {
  return modelCategory.value === 'lstm' ?
    'LSTM模型使用递归神经网络进行时间序列预测' :
    '集成学习模型使用多个弱学习器组合成强学习器进行预测';
};

const getR2Value = () => {
  return evaluationResult.value?.metrics.R2 || 0;
};

const exportToWord = async () => {
  if (!evaluationResult.value) return;

  isExporting.value = true;

  try {
    // 1. 准备图表图片
    let plotImageBuffer: ArrayBuffer | null = null;
    try {
      // 尝试从DOM获取图表
      const plotElement = document.querySelector('.evaluation-plot') as HTMLElement;
      if (plotElement) {
        const plotUrl = await toPng(plotElement, {
          quality: 1,
          pixelRatio: 2
        });
        plotImageBuffer = await fetch(plotUrl).then(res => res.arrayBuffer());
      }
    } catch (imgError) {
      console.warn('图表转换失败:', imgError);
      // 如果DOM转换失败，尝试使用原始URL
      if (evaluationResult.value.plot_url) {
        try {
          const plotUrl = getFullPlotUrl(evaluationResult.value.plot_url);
          plotImageBuffer = await fetch(plotUrl).then(res => res.arrayBuffer());
        } catch (fetchError) {
          console.error('获取图表失败:', fetchError);
        }
      }
    }

    // 2. 创建Word文档
    const doc = new DocxDocument({
      sections: [{
        properties: {},
        children: [
          // 报告标题
          new Paragraph({
            text: "模型评估报告",
            heading: HeadingLevel.HEADING_1,
            spacing: { after: 200 },
            alignment: AlignmentType.CENTER
          }),

          // 基本信息
          new Paragraph({
            text: `生成时间: ${new Date().toLocaleString()}`,
            spacing: { after: 200 }
          }),

          new Paragraph({
            text: `模型类型: ${getModelDisplayName()}`,
            spacing: { after: 200 }
          }),

          ...(modelType.value === ModelType.ENSEMBLE ? [
            new Paragraph({
              text: `测试天数: ${testDays.value}`,
              spacing: { after: 400 }
            })
          ] : []),

          // 评估指标
          new Paragraph({
            text: "评估指标",
            heading: HeadingLevel.HEADING_2,
            spacing: { after: 200 }
          }),

          // 指标表格
          new Table({
            rows: [
              createTableHeader(["指标", "值", "解释"]),
              createMetricRow("MAE", evaluationResult.value.metrics.MAE, "平均绝对误差"),
              createMetricRow("R²", evaluationResult.value.metrics.R2, "决定系数"),
              createMetricRow("RMSE", evaluationResult.value.metrics.RMSE, "均方根误差"),
              ...(modelType.value === ModelType.ENSEMBLE ? [
                createMetricRow("测试天数", evaluationResult.value.metrics.test_days, "评估天数")
              ] : []),
              createMetricRow("预测目标",
                targetOptions.find(t => t.value === selectedTarget.value)?.label || selectedTarget.value,
                "预测的目标变量")
            ],
            width: { size: 100, type: 'pct' },
            margins: { top: 100, bottom: 100 }
          }),

          // 图表部分
          new Paragraph({
            text: "评估图表",
            heading: HeadingLevel.HEADING_2,
            spacing: { before: 400, after: 200 }
          }),

          // 插入图表（如果有）
          ...(plotImageBuffer ? [
            new Paragraph({
              children: [
                new ImageRun({
                  data: plotImageBuffer,
                  transformation: {
                    width: 450,
                    height: 300,
                  },
                }),
              ],
              alignment: AlignmentType.CENTER,
              spacing: { after: 200 }
            }),
            new Paragraph({
              text: `图1. 模型预测效果对比（蓝色：实际值，红色：预测值${modelType.value === ModelType.LSTM ? '' : '，绿色点为最大误差位置'}）`,
              alignment: AlignmentType.CENTER,
              italics: true,
              size: 20,
              spacing: { after: 400 }
            })
          ] : [
            new Paragraph({
              text: "图表加载失败，请检查网络连接或重新生成报告",
              color: "FF0000",
              spacing: { after: 400 }
            })
          ]),

          // 结论部分
          new Paragraph({
            text: "分析结论",
            heading: HeadingLevel.HEADING_2,
            spacing: { after: 200 }
          }),

          new Paragraph({
            children: [
              new TextRun({
                text: "模型性能评估结论：",
                bold: true
              }),
              new TextRun({
                text: getR2Value() >= 0.7 ?
                  " 该模型表现优秀，预测结果可靠。" :
                  getR2Value() >= 0.4 ?
                    " 该模型表现一般，建议进一步优化。" :
                    " 该模型表现不佳，不建议直接使用。",
                size: 24
              })
            ],
            spacing: { after: 200 }
          }),

          // 备注
          new Paragraph({
            text: "备注: 本报告由系统自动生成，评估结果仅供参考。",
            italics: true,
            color: "666666"
          })
        ]
      }]
    });

    // 3. 生成并下载文档
    const blob = await Packer.toBlob(doc);
    saveAs(blob, `模型评估报告_${modelCategory.value}_${selectedTarget.value}_${new Date().toISOString().slice(0, 10)}.docx`);
    ElMessage.success('Word报告导出成功');

  } catch (error) {
    console.error('导出失败:', error);
    ElMessage.error(`报告导出失败: ${error.message}`);
  } finally {
    isExporting.value = false;
  }
};

// 辅助函数
const createTableHeader = (headers: string[]) => {
  return new TableRow({
    children: headers.map(header =>
      new TableCell({
        children: [new Paragraph({
          text: header,
          bold: true
        })],
        shading: {
          fill: "DDDDDD"
        }
      })
    )
  });
};

const createMetricRow = (name: string, value: any, description: string) => {
  return new TableRow({
    children: [
      new TableCell({
        children: [new Paragraph({
          text: name,
          bold: true
        })]
      }),
      new TableCell({
        children: [new Paragraph(
          typeof value === 'number' ? value.toFixed(4) : String(value)
        )]
      }),
      new TableCell({
        children: [new Paragraph(description)]
      })
    ]
  });
};

// 监控评估结果变化
watch(evaluationResult, (newVal) => {
  console.log('评估结果更新:', newVal);
  if (newVal?.plot_url) {
    console.log('图片URL:', getFullPlotUrl(newVal.plot_url));
  }
}, { deep: true });
</script>

<style scoped lang="scss">
.model-evaluation-container {
  padding: 24px;
  max-width: 1400px;
  margin: 0 auto;

  .header {
    margin-bottom: 24px;

    .title-description {
      margin-bottom: 16px;

      h2 {
        color: var(--el-text-color-primary);
        margin-bottom: 8px;
      }

      .description {
        color: var(--el-text-color-secondary);
        font-size: 14px;
        margin: 0;
      }
    }

    .control-panel {
      display: flex;
      gap: 16px;
      align-items: center;
      flex-wrap: wrap;

      .el-select,
      .el-input-number {
        width: 180px;
      }

      .el-button {
        position: relative;

        .el-icon {
          margin-right: 4px;
        }
      }
    }
  }

  .error-message {
    margin-bottom: 24px;
  }

  .result-container {
    display: grid;
    grid-template-columns: 1fr;
    gap: 24px;
    margin-top: 16px;

    .el-card {
      border-radius: 8px;
      box-shadow: var(--el-box-shadow-light);

      :deep(.el-card__header) {
        border-bottom: 1px solid var(--el-border-color-light);
        padding: 16px 20px;

        .card-header {
          display: flex;
          justify-content: space-between;
          align-items: center;

          h3 {
            margin: 0;
            font-size: 18px;
          }

          .header-tags {
            display: flex;
            align-items: center;
            gap: 8px;
          }
        }
      }

      :deep(.el-card__body) {
        padding: 20px;
      }
    }

    .metrics-card {
      .metrics-grid {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        gap: 16px;

        @media (max-width: 992px) {
          grid-template-columns: repeat(2, 1fr);
        }

        @media (max-width: 768px) {
          grid-template-columns: 1fr;
        }
      }

      .metric-item {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 16px;
        background-color: var(--el-fill-color-light);
        border-radius: 8px;
        transition: all 0.3s ease;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);

        &:hover {
          background-color: var(--el-fill-color);
          transform: translateY(-3px);
          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
        }

        .metric-label-container {
          display: flex;
          align-items: center;
          gap: 4px;
        }

        .metric-label {
          font-size: 14px;
          color: var(--el-text-color-secondary);
        }

        .metric-value {
          font-weight: bold;
          display: flex;
          align-items: center;
          gap: 4px;

          &.negative {
            color: var(--el-color-danger);
          }
        }

        .help-icon,
        .value-hint {
          color: var(--el-text-color-placeholder);
          cursor: help;
          font-size: 14px;
          transition: color 0.2s;

          &:hover {
            color: var(--el-color-primary);
          }
        }
      }
    }

    .action-buttons {
      margin-top: 0;
      justify-content: flex-start;

      .el-button {
        transition: all 0.3s ease;

        &:hover:not([disabled]) {
          transform: translateY(-2px);
          box-shadow: 0 4px 12px rgba(103, 194, 58, 0.2);
        }
      }
    }

    .plot-card {
      .plot-container {
        height: 400px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        position: relative;

        .evaluation-plot {
          max-width: 100%;
          max-height: 100%;
          object-fit: contain;
          border-radius: 4px;
          opacity: v-bind('imageLoaded ? 1 : 0');
          transition: opacity 0.3s;
          cursor: zoom-in;
          transition: transform 0.3s;

          &:hover {
            transform: scale(1.02);
          }
        }

        .el-skeleton {
          position: absolute;
          width: 100%;
        }

        .plot-footer {
          margin-top: 12px;
          width: 100%;
          text-align: center;
        }
      }
    }
  }

  .empty-state {
    margin-top: 40px;
    text-align: center;

    p {
      color: var(--el-text-color-secondary);
      margin-top: 8px;
    }
  }
}
</style>
